using System.Collections.Generic;
using System.Diagnostics;
using Fadd.Parser.SimpleSQL.ParseTreeTokens;

#if TEST
using Fadd.Parser.SimpleSQL.Tokens;
using Xunit;
#endif

namespace Fadd.Parser.SimpleSQL
{
    /// <summary>
    /// Used to parse SQL conditions
    /// </summary>
    public class Parser
    {
        private Tokenizer _tokenizer;

        /// <summary>
        /// Chars interpreted as white spaces: NewLine,LineFeed,Tab,Space
        /// </summary>
        public const string WhiteSpaces = "\r\n\t ";

        /// <summary>
        /// Initializes a new instance of the <see cref="Parser"/> class.
        /// </summary>
        public Parser()
        {
            Setup();
        }

        /// <summary>
        /// Setup all tokens
        /// </summary>
        private void Setup()
        {
            _tokenizer = new Tokenizer();

            AddSelectStatement();
            AddWhereStatement();
            AddFromStatement();
            AddDeleteStatement();
            AddOrderByStatement();
        }

        private void AddDeleteStatement()
        {
            DeleteToken token = new DeleteToken();
            token.Add(new ClassToken());
            token.Add(new CommaToken());
            _tokenizer.Prototypes.Add(token);
        }

        private void AddSelectStatement()
        {
            SelectToken token = new SelectToken();
            token.Add(new CountToken());
            token.Add(new PropertyToken());
            token.Add(new CommaToken());
            token.Add(new StarToken());
            _tokenizer.Prototypes.Add(token);
        }

        private void AddWhereStatement()
        {
            WhereToken token = new WhereToken();
            AddGeneralWhereTokens(token);
            _tokenizer.Prototypes.Add(token);

            StartContext context = new StartContext();
            AddGeneralWhereTokens(context);
            token.Add(context);
            token.Add(new StopContext());
        }

        private void AddGeneralWhereTokens(ParseTreeToken token)
        {
            token.Add(new AndToken());
            token.Add(new EqualToken());
            token.Add(new InToken());
            token.Add(new LessOrEqualToken());
            token.Add(new LessThanToken());
            token.Add(new MoreOrEqualToken());
            token.Add(new MoreThanToken());
            token.Add(new NotEqualToken());
            token.Add(new OrToken());
            token.Add(new PropertyToken());
            token.Add(new PropertyValueToken());
            token.Add(new LikeToken());
            token.Add(new BitwiseToken());            
			token.Add(new IsNotNullToken());
        }

        private void AddFromStatement()
        {
            FromToken token = new FromToken();
            token.Add(new ClassToken());
            token.Add(new CommaToken());
            _tokenizer.Prototypes.Add(token);
        }

        private void AddOrderByStatement()
        {
            OrderByToken token = new OrderByToken();
            token.Add(new OrderByPropertyToken());
            token.Add(new CommaToken());
            _tokenizer.Prototypes.Add(token);
        }
        /// <summary>
        /// Parse the tree
        /// </summary>
        /// <param name="text">Text to parse.</param>
        /// <returns>a parsed tree</returns>
        public ParseTree Parse(string text)
        {
            ParseTree tree = new ParseTree();
            _tokenizer.Parse(text, tree);
            return tree;
        }

#if TEST
        [Fact]
        private void TestSimpleSelect()
        {
            ParseTree tree = Parse("SELECT UserName FROM TN");
            PrintTree(tree);
            Assert.IsType(typeof (Select), tree[0]);
            Assert.IsType(typeof (Tokens.Property), tree[0][0]);
            Assert.IsType(typeof (From), tree[1]);
            Assert.IsType(typeof(Class), tree[1][0]);
        }

        [Fact]
        private void TestSelectWithCount()
        {
            ParseTree tree = Parse("SELECT COUNT(*) FROM TN");
            PrintTree(tree);
            Assert.IsType(typeof (Select), tree[0]);
            Assert.IsType(typeof (Count), tree[0][0]);
            Assert.IsType(typeof (From), tree[1]);
            Assert.IsType(typeof (Class), tree[1][0]);
        }

        [Fact]
        private void TestWithWhere()
        {
            ParseTree tree = Parse("SELECT * FROM Table1, Table2 WHERE FirstName = ?");
            PrintTree(tree);
        }

        [Fact]
        private void TestMultipleColumns()
        {
            ParseTree tree = Parse("SELECT Column1, Column2 FROM Table1, Table2 WHERE FirstName = ?");
            PrintTree(tree);
        }

        [Fact]
        private void TestOrderBy()
        {
            ParseTree tree = Parse("SELECT Column1, Column2 FROM Table1, Table2 WHERE FirstName = ? ORDER BY FirstName ");
        }

        [Fact]
        private void TestSelect()
        {
            ParseTree tree =
                Parse("SELECT * FROM User WHERE FirstName LIKE ? OR LastName LIKE ? ORDER BY System.String[]");
        }

        [Fact]
        private void TestDelete()
        {
            ParseTree tree =
                Parse("DELETE FROM UserLogin WHERE UserId = ?");
        }

        [Fact]
        private void TestBitWiseSimple()
        {
            ParseTree tree = Parse("WHERE (AccountType & 4 <> ?)");
        }

        [Fact]
        private void TestBitWise()
        {

            ParseTree tree2 = Parse("WHERE ArtistName LIKE ? AND (AccountType & 4) <> ?");
            
        }

		[Fact]
		private void TestIsNotNull()
		{

			ParseTree tree2 = Parse("WHERE ArtistName IS NOT NULL");

		}
#endif
        private static void PrintTree(IEnumerable<Token> tree)
        {
            foreach (Token token in tree)
                PrintTree(token, 0);
        }

        private static void PrintTree(Token token, int spaces)
        {
            Debug.Write(string.Empty.PadLeft(spaces));
            Debug.WriteLine(token.ToString());
            foreach (Token child in token.Children)
                PrintTree(child, spaces + 2);
        }
    }
}
